﻿using AutoRegistDependency.Abstract;
using AutoRegistDependency.Attributes;
using AutoRegistDependency.Component;
using AutoRegistDependency.Enum;
using AutoRegistDependency.Interface;
using AutoRegistDependency.Utils;
using Castle.DynamicProxy;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Castle.Components.DictionaryAdapter;
using ComponentAttribute = AutoRegistDependency.Attributes.ComponentAttribute;

namespace AutoRegistDependency.Implement
{
    public class DefaultRegistrationInitializer : IRegistrationInitializer
    {
        public List<AbstractInterceptor> Interceptors { get; set; } = new EditableList<AbstractInterceptor>();

        public virtual ConcurrentDictionary<Type, List<ComponentDefinition>> CreateDefinitionDictionary(IEnumerable<Assembly> assemblies, LifeTimeType globalLifeTimeType, PropertyInjectType globalPropertyInjectType, RegistrationType globalRegistrationType)
        {
            var result = new ConcurrentDictionary<Type, List<ComponentDefinition>>();
            assemblies = assemblies.ToList();
            var types = assemblies.SelectMany(t => t.GetTypes())
                .Where(t => !t.IsAbstract && t.IsClass && t.GetCustomAttributes().Any(a => a is ComponentAttribute || a is ConfigurationPropertiesAttribute || a is ConfigurationAttribute || a is JobAttribute))
                .Union(assemblies.SelectMany(t => t.GetTypes()).Where(t => t.IsInterface && t.GetCustomAttributes().Any(a => a is AggregateServiceAttribute)))
                .OrderBy(t =>
                {
                    var attr = t.GetCustomAttribute<OrderAttribute>();
                    if (attr != null)
                    {
                        return attr.Order;
                    }
                    return 999;
                })
                .ThenBy(t => t.Name)
                .ToList();
            foreach (Type type in types)
            {
                //服务扫描
                var registrationInfos = new ComponentDefinition(type, globalLifeTimeType, globalPropertyInjectType, globalRegistrationType)
                    .GetComponentTypeRegistInfos();
                foreach (ComponentDefinition registrationInfo in registrationInfos)
                {
                    if (type.IsSubclassOf(typeof(AbstractInterceptor)))
                    {
                        //取参数最少的构造函数new一个
                        var constructor = type.GetConstructors().OrderBy(t => t.GetParameters()).First();
                        var constructorParameters = constructor.GetParameters();
                        object[] invokeParameters = new object[constructorParameters.Length];
                        for (int i = 0; i < constructor.GetParameters().Length; i++)
                        {
                            invokeParameters[i] = AutoFacHelper.GetDefaultValue(constructorParameters[i].ParameterType);
                        }
                        AbstractInterceptor interceptor = (AbstractInterceptor)constructor.Invoke(invokeParameters);
                        Interceptors.Add(interceptor);
                    }
                    if (result.TryGetValue(registrationInfo.InjectType, out var componentTypeRegistrationInfos))
                    {
                        componentTypeRegistrationInfos.Add(registrationInfo);
                    }
                    else
                    {
                        result.TryAdd(registrationInfo.InjectType, new List<ComponentDefinition>() { registrationInfo });
                    }
                }
                AppendInterceptor(assemblies,result);
            }

            return result;
        }

        private void AppendInterceptor(IEnumerable<Assembly> assemblies,
            ConcurrentDictionary<Type, List<ComponentDefinition>> map)
        {
            var dll = assemblies.ToList();
            foreach (var abstractInterceptor in Interceptors)
            {
                var interceptTypes = abstractInterceptor.GetInterceptorTypes(dll);
                foreach (var interceptType in interceptTypes)
                {
                    if (map.TryGetValue(interceptType, out List<ComponentDefinition> components))
                    {
                        components.ForEach(t=>t.Interceptors.Add(abstractInterceptor));
                    }
                }
            }
        }
    }
}
